#include "common/ob_define.h"
#include "common/ob_schema.h"
#include "ob_schema_manager.h"

using namespace oceanbase::common;

ObMergerSchemaManager::ObMergerSchemaManager()
{
  init_ = false;
  update_timestamp_ = 0;
  latest_version_ = -1;
}

ObMergerSchemaManager::~ObMergerSchemaManager()
{
}

// init in main thread
int ObMergerSchemaManager::init(const bool system, const ObSchemaManagerV2 & schema)
{
  int ret = OB_SUCCESS;
  if (true == init_)
  {
    TBSYS_LOG(WARN, "%s", "init schema manager twice");
    ret = OB_INIT_TWICE;
  }
  else if (false == system)
  {
    user_schemas_[0].ref_count = 1;
    user_schemas_[0].schema = schema;
    update_timestamp_ = tbsys::CTimeUtil::getTime();
    latest_version_ = schema.get_version();
    TBSYS_LOG(INFO, "init user schema manager succ:timestamp[%ld]", latest_version_);
    init_ = true;
  }
  else
  {
    sys_schema_.ref_count = 1;
    sys_schema_.schema = schema;
    TBSYS_LOG(INFO, "init sys schema manager succ:timestamp[%ld]", schema.get_version());
    init_ = true;
  }
  return ret;
}

// schema version must incremental
//schema 版本必须为增加的
//manager为NULL
int ObMergerSchemaManager::add_schema(const ObSchemaManagerV2 & schema,
    const ObSchemaManagerV2 ** manager)
{

  int ret = OB_SUCCESS;
  if (false == check_inner_stat())
  {
    TBSYS_LOG(ERROR, "%s", "check inner stat or schema version failed");
    ret = OB_INNER_STAT_ERROR;
  }
  else
  {
    tbsys::CThreadGuard lock(&lock_);
    //再次检查版本号  如果本地的版本号已经是最大  则不更新
    if (latest_version_ >= schema.get_version())
    {
      TBSYS_LOG(WARN, "check schema version failed:latest[%ld], schema[%ld]",
          latest_version_, schema.get_version());
      ret = OB_OLD_SCHEMA_VERSION;
    }
    else
    {
      // find replace position for the new schema
      // at first not inited, then not used and the oldest version pos
      int64_t pos = find_replace_pos();
      if (pos >= 0)
      {
//          ObTableSchema* tempSchema;  
//          ObTableSchema* tempSchema2;
 //         ObSchemaManagerV2 * tempMgrSchema;
//          //如果schema里的table schema数组存储的只是被更新的table schema
//          //则在这里要做修改
//          //step1.获取本地最新版本的schema  manager 从而得到table schema
//          //step2.遍历本地的table schema,根据接收的schema manager里的table  schema,进行查找
//          //step3.修改table schema，直接修改指针
//          //step4.user_schemas_[pos].schema = schema;//此时的schema已经被修改
 //         tempMgrSchema = this->get_user_schema(latest_version_);
//          ObTableSchema* begin = schema.table_begin();
//          int64_t table_num=schema.get_table_count();
//          for(int i=0;i<table_num;i++)
//          {

//              ObTableSchema* temp = begin+i;
//              int64_t tableID = temp->get_table_id();
//              ObTableSchema* left=tempMgrSchema->get_table_schema(tableID);
//              if(left==NULL)
//              {
//                  //添加
//                  tempMgrSchema->add_new_table_schema(left);
//              }
//              else
//              {
//                  //tempMgrSchema->
//              }
//          }
  //        tempMgrSchema->add_new_table_schema(table_schema_array);
//          for(int i=0;i<schema.get_table_count();i++)
//          {
//              //要更新的tableSchema
//             tempSchema = schema.table_begin()+i;
//             //本地的tableSchema指针
//             tempSchema2 = tempMgrSchema->get_table_schema(tempSchema->get_table_id());
//            if(tempSchema2 != NULL)
//            {//直接改变指针
//                //该表已经存在   则为修改
//             //释放原来的指针
//                this->release_schema(tempSchema2);
//            }
//            else{//该表不存在  则为
//                //在schemaManager中添加新的tableSchema
//            }
//             tempMgrSchema->add_new_table_schema(tempSchema);
//          }
//          schema = tempMgrSchema;

        user_schemas_[pos].schema = schema;
        //ref_count代表有无使用
        //1代表// not empty but not using right now
        user_schemas_[pos].ref_count = 1;
        //更新时间
        update_timestamp_ = tbsys::CTimeUtil::getTime();
        //更新最新的版本
        latest_version_ = schema.get_version();
        if (NULL != manager)
        {
            //2代表不为空  且已经使用
          user_schemas_[pos].ref_count = 2;
          *manager = &user_schemas_[pos].schema;
        }
      }
      //没有可以使用的空间
      else
      {
        ret = OB_NO_EMPTY_ENTRY;
      }
    }
  }
  return ret;
}

int64_t ObMergerSchemaManager::find_replace_pos(void) const
{
  int64_t pos = -1;
  int64_t old_version = MAX_INT64_VALUE;
  for (uint64_t i = 0; i < MAX_VERSION_COUNT; ++i)
  {
    // empty pos
    if (0 == user_schemas_[i].ref_count)
    {
      pos = i;
      break;
    }
    // not empty but not using right now
    else if (1 == user_schemas_[i].ref_count)
    {
      if (old_version > user_schemas_[i].schema.get_version())
      {
        old_version = user_schemas_[i].schema.get_version();
        pos = i;
      }
      continue;
    }
  }
  return pos;
}

const ObSchemaManagerV2 * ObMergerSchemaManager::get_schema(const common::ObString & table_name)
{
  const ObSchemaManagerV2 * schema = NULL;
  const ObTableSchema * table = sys_schema_.schema.get_table_schema(table_name);
  if (table != NULL)
  {
    // get system table schema
    schema = &sys_schema_.schema;
  }
  else
  {
    // get local newest user table schema
    schema = get_user_schema(0);
  }
  return schema;
}

const ObSchemaManagerV2 * ObMergerSchemaManager::get_schema(const uint64_t table_id)
{
  const ObSchemaManagerV2 * schema = NULL;
  const ObTableSchema * table = sys_schema_.schema.get_table_schema(table_id);
  if (table != NULL)
  {
    // get system table schema
    schema = &sys_schema_.schema;
  }
  else
  {
    // get local newest user table schema
    schema = get_user_schema(0);
  }
  return schema;
}

const ObSchemaManagerV2 * ObMergerSchemaManager::get_user_schema(const int64_t version)
{
  const ObSchemaManagerV2 * schema = NULL;
  if (false == check_inner_stat())
  {
    TBSYS_LOG(ERROR, "%s", "check inner stat failed");
  }
  else
  {
    int64_t new_version = version;
    // get the latest version
    tbsys::CThreadGuard lock(&lock_);
    if (0 == version)
    {
      new_version = latest_version_;
    }
    //
    for (uint64_t i = 0; i < MAX_VERSION_COUNT; ++i)
    {
      if (0 == user_schemas_[i].ref_count)
      {
        continue;
      }
      else
      {
        if (user_schemas_[i].schema.get_version() == new_version)
        {
          ++user_schemas_[i].ref_count;
          schema = &user_schemas_[i].schema;
        }
      }
    }
  }
  return schema;
}

int ObMergerSchemaManager::release_schema(const ObSchemaManagerV2 * schema)
{
  int ret = OB_RELEASE_SCHEMA_ERROR;
  if (false == check_inner_stat())
  {
    TBSYS_LOG(ERROR, "check inner stat or release version failed");
    ret = OB_INNER_STAT_ERROR;
  }
  else if (NULL == schema)
  {
    TBSYS_LOG(WARN, "check input param schema failed");
    ret = OB_INVALID_ARGUMENT;
  }
  else if (schema != &(sys_schema_.schema))
  {
    bool find = false;
    tbsys::CThreadGuard lock(&lock_);
    for (uint64_t i = 0; i < MAX_VERSION_COUNT; ++i)
    {
      // 0 is empty, 1 is not used
      if (user_schemas_[i].ref_count <= 1)
      {
        continue;
      }
      else
      {
        if (&user_schemas_[i].schema == schema)
        {
          --user_schemas_[i].ref_count;
          assert(user_schemas_[i].ref_count > 0);
          find = true;
          ret = OB_SUCCESS;
          break;
        }
      }
    }
    assert(find == true);
  }
  else
  {
    ret = OB_SUCCESS;
  }
  
  if (OB_SUCCESS != ret)
  {
    TBSYS_LOG(ERROR, "fail to release schema. schema=%p", schema);
  }
  return ret;
}

int64_t ObMergerSchemaManager::get_oldest_version(void) const
{
  int64_t version = MAX_INT64_VALUE;
  tbsys::CThreadGuard lock(&lock_);
  for (uint64_t i = 0; i < MAX_VERSION_COUNT; ++i)
  {
    if (0 == user_schemas_[i].ref_count)
    {
      continue;
    }
    else
    {
      if (user_schemas_[i].schema.get_version() < version)
      {
        version = user_schemas_[i].schema.get_version();
      }
    }
  }
  return version;
}

int ObMergerSchemaManager::print_info(void) const
{
  int ret = OB_SUCCESS;
  if (false == check_inner_stat())
  {
    TBSYS_LOG(ERROR, "%s", "check inner stat failed");
    ret = OB_INNER_STAT_ERROR;
  }
  else
  {
    tbsys::CThreadGuard lock(&lock_);
    TBSYS_LOG(INFO, "update[%ld], latest[%ld]", update_timestamp_, latest_version_);
    for (uint64_t i = 0; i < MAX_VERSION_COUNT; ++i)
    {
      if (0 == user_schemas_[i].ref_count)
      {
        continue;
      }
      else
      {
        TBSYS_LOG(INFO, "schema ref_count[%ld]", user_schemas_[i].ref_count);
        user_schemas_[i].schema.print_info();
      }
    }
  }
  return ret;
}
